<!doctype html>



  


<html class="theme-next pisces use-motion">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />












  
  
  <link href="/vendors/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Courier New:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/vendors/font-awesome/css/font-awesome.min.css?v=4.4.0" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.0.1" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="JavaScript," />





  <link rel="alternate" href="/atom.xml" title="Never_yu's Blog" type="application/atom+xml" />




  <link rel="shortcut icon" type="image/x-icon" href="/favicon/favicon.ico?v=5.0.1" />






<meta name="description" content="前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。
一、为什么要处理异常？异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。

1.增强用户体验；2.远程定位问题；3.未雨绸缪，及早发现问题；4.无法复线问题，尤其是移动端，机型，系统都是问题；5.完善的前端方案，前端监控系统；">
<meta property="og:type" content="article">
<meta property="og:title" content="【转】如何优雅处理前端异常">
<meta property="og:url" content="https://neveryu.github.io/2019/05/01/js-exception-handing/index.html">
<meta property="og:site_name" content="Never_yu's Blog">
<meta property="og:description" content="前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。
一、为什么要处理异常？异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。

1.增强用户体验；2.远程定位问题；3.未雨绸缪，及早发现问题；4.无法复线问题，尤其是移动端，机型，系统都是问题；5.完善的前端方案，前端监控系统；">
<meta property="og:image" content="https://neveryu.github.io/images/js-exception-handing-1.png">
<meta property="og:image" content="https://neveryu.github.io/images/js-exception-handing-2.png">
<meta property="og:image" content="https://neveryu.github.io/images/js-exception-handing-3.png">
<meta property="og:updated_time" content="2019-05-17T14:12:45.148Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="【转】如何优雅处理前端异常">
<meta name="twitter:description" content="前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。
一、为什么要处理异常？异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。

1.增强用户体验；2.远程定位问题；3.未雨绸缪，及早发现问题；4.无法复线问题，尤其是移动端，机型，系统都是问题；5.完善的前端方案，前端监控系统；">
<meta name="twitter:image" content="https://neveryu.github.io/images/js-exception-handing-1.png">



<script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"hide"},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: undefined,
      author: '博主'
    }
  };
</script>




  <link rel="canonical" href="https://neveryu.github.io/2019/05/01/js-exception-handing/"/>


<!-- 网页加载条 -->
<script src="/js/src/pace.min.js"></script>
  <title> 【转】如何优雅处理前端异常 | Never_yu's Blog </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  










  
  
    
  

  <div class="container one-collumn sidebar-position-left page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-meta ">
  

  <div class="custom-logo-site-title">
    <a href="/"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">Never_yu's Blog</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">认真的人才有资格开玩笑</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-guestbook">
          <a href="/guestbook" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-commenting"></i> <br />
            
            留言
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-example">
          <a href="/example" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            示例
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
      
      
        <li class="menu-item"> <a title="把这个链接拖到你的工具栏中,任何网页都可以High" href='javascript:(
/*
 * Copyright (C) 2016 Never_yu (Neveryu.github.io) <React.dong.yu@gmail.com>
 * Sina Weibo (http://weibo.com/Neveryu)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function go() {

var songs = [
  "http://dl.stream.qqmusic.qq.com/C400001Qu4I30eVFYb.m4a?vkey=2127178E4405E7B8B268F20F05232485735CCF4FF8C1432F0360D2626D0B6C9564B5627C7AB481BBC685FEDB0946A50E97C66F0D1B008226&guid=7175649092&uin=0&fromtag=66",
  "",
  "",
  ""
];

function c() {
  var e = document.createElement("link");
  e.setAttribute("type", "text/css");
  e.setAttribute("rel", "stylesheet");
  e.setAttribute("href", f);
  e.setAttribute("class", l);
  document.body.appendChild(e)
}

function h() {
  var e = document.getElementsByClassName(l);
  for (var t = 0; t < e.length; t++) {
    document.body.removeChild(e[t])
  }
}

function p() {
  var e = document.createElement("div");
  e.setAttribute("class", a);
  document.body.appendChild(e);
  setTimeout(function() {
    document.body.removeChild(e)
  }, 100)
}

function d(e) {
  return {
    height : e.offsetHeight,
    width : e.offsetWidth
  }
}

function v(i) {
  var s = d(i);
  return s.height > e && s.height < n && s.width > t && s.width < r
}

function m(e) {
  var t = e;
  var n = 0;
  while (!!t) {
    n += t.offsetTop;
    t = t.offsetParent
  }
  return n
}

function g() {
  var e = document.documentElement;
  if (!!window.innerWidth) {
    return window.innerHeight
  } else if (e && !isNaN(e.clientHeight)) {
    return e.clientHeight
  }
  return 0
}

function y() {
  if (window.pageYOffset) {
    return window.pageYOffset
  }
  return Math.max(document.documentElement.scrollTop, document.body.scrollTop)
}

function E(e) {
  var t = m(e);
  return t >= w && t <= b + w
}

function S() {
  var e = document.getElementById("audio_element_id");
  if(e != null){
    var index = parseInt(e.getAttribute("curSongIndex"));
    if(index > songs.length - 2) {
      index = 0;
    } else {
      index++;
    }
    e.setAttribute("curSongIndex", index);
    N();
  }

  e.src = i;
  e.play()
}

function x(e) {
  e.className += " " + s + " " + o
}

function T(e) {
  e.className += " " + s + " " + u[Math.floor(Math.random() * u.length)]
}

function N() {
  var e = document.getElementsByClassName(s);
  var t = new RegExp("\\b" + s + "\\b");
  for (var n = 0; n < e.length; ) {
    e[n].className = e[n].className.replace(t, "")
  }
}

function initAudioEle() {
  var e = document.getElementById("audio_element_id");
  if(e === null){
    e = document.createElement("audio");
    e.setAttribute("class", l);
    e.setAttribute("curSongIndex", 0);
    e.id = "audio_element_id";
    e.loop = false;
    e.bgcolor = 0;
    e.addEventListener("canplay", function() {
      setTimeout(function() {
        x(k)
      }, 500);
      setTimeout(function() {
        N();
        p();
        for (var e = 0; e < O.length; e++) {
          T(O[e])
        }
      }, 15500)
    }, true);
    e.addEventListener("ended", function() {
      N();
      h();
      go();
    }, true);
    e.innerHTML = " <p>If you are reading this, it is because your browser does not support the audio element. We recommend that you get a new browser.</p> <p>";
    document.body.appendChild(e);
  }
}

initAudioEle();
var e = 30;
var t = 30;
var n = 350;
var r = 350;

var curSongIndex = parseInt(document.getElementById("audio_element_id").getAttribute("curSongIndex"));
var i = songs[curSongIndex];

var s = "mw-harlem_shake_me";
var o = "im_first";
var u = ["im_drunk", "im_baked", "im_trippin", "im_blown"];
var a = "mw-strobe_light";

/* harlem-shake-style.css，替换成你的位置，也可以直接使用：//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake-style.css */
var f = "//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake-style.css";

var l = "mw_added_css";
var b = g();
var w = y();
var C = document.getElementsByTagName("*");
var k = null;
for (var L = 0; L < C.length; L++) {
  var A = C[L];
  if (v(A)) {
    if (E(A)) {
      k = A;
      break
    }
  }
}
if (A === null) {
  console.warn("Could not find a node of the right size. Please try a different page.");
  return
}
c();
S();
var O = [];
for (var L = 0; L < C.length; L++) {
  var A = C[L];
  if (v(A)) {
    O.push(A)
  }
}
})()'><i class="menu-item-icon fa fa-music fa-fw"></i> High一下</a></li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup">
 <span class="search-icon fa fa-search fa-lg"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>


    </div>
  
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  
  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                【转】如何优雅处理前端异常
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            <span class="post-meta-item-icon">
              <i class="fa fa-calendar-o"></i>
            </span>
            <span class="post-meta-item-text">发表于</span>
            <time itemprop="dateCreated" datetime="2019-05-01T03:25:24+08:00" content="2019-05-01">
              2019-05-01
            </time>
          </span>

          
            <span class="post-category" >
              &nbsp; | &nbsp;
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
              
                <span itemprop="about" itemscope itemtype="https://schema.org/Thing">
                  <a href="/categories/前端/" itemprop="url" rel="index">
                    <span itemprop="name">前端</span>
                  </a>
                </span>

                
                

              
            </span>
          

          

          

          
          

          
              &nbsp; | &nbsp;
              <span class="page-pv">热度
              <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>℃
              </span>
          
        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。</p>
<h1 id="一、为什么要处理异常？"><a href="#一、为什么要处理异常？" class="headerlink" title="一、为什么要处理异常？"></a>一、为什么要处理异常？</h1><p id="div-border-left-green">异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。</p>

<p>1.增强用户体验；<br>2.远程定位问题；<br>3.未雨绸缪，及早发现问题；<br>4.无法复线问题，尤其是移动端，机型，系统都是问题；<br>5.完善的前端方案，前端监控系统；</p>
<a id="more"></a>
<p>对于 JS 而言，我们面对的仅仅只是异常，异常的出现不会直接导致 JS 引擎崩溃，最多只会使当前执行的任务终止。</p>
<h1 id="二、需要处理哪些异常？"><a href="#二、需要处理哪些异常？" class="headerlink" title="二、需要处理哪些异常？"></a>二、需要处理哪些异常？</h1><p>对于前端来说，我们可做的异常捕获还真不少。总结一下，大概如下：</p>
<ul>
<li>JS 语法错误、代码异常</li>
<li>AJAX 请求异常</li>
<li>静态资源加载异常</li>
<li>Promise 异常</li>
<li>Iframe 异常</li>
<li>跨域 Script error</li>
<li>崩溃和卡顿</li>
</ul>
<p id="div-border-left-yellow">下面我会针对每种具体情况来说明如何处理这些异常。</p>


<h1 id="三、Try-Catch-的误区"><a href="#三、Try-Catch-的误区" class="headerlink" title="三、Try-Catch 的误区"></a>三、Try-Catch 的误区</h1><p id="div-border-top-blue"><code>try-catch</code> 只能捕获到 <font color="red"><strong>同步</strong></font> 的运行时错误，对 <font color="red">语法</font> 和 <font color="red">异步</font> 错误却无能为力，捕获不到。【不能捕获XHR，AJAX的异常】</p>

<p>1.同步运行时错误：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> name = <span class="string">'jartto'</span>;</span><br><span class="line">  <span class="built_in">console</span>.log(nam);</span><br><span class="line">&#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">捕获到异常： <span class="built_in">ReferenceError</span>: nam is not defined at &lt;anonymous&gt;:<span class="number">3</span>:<span class="number">15</span></span><br></pre></td></tr></table></figure></p>
<p>2.不能捕获到语法错误，我们修改一下代码，删掉一个单引号：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> name = <span class="string">'jartto;</span></span><br><span class="line"><span class="string">  console.log(nam);</span></span><br><span class="line"><span class="string">&#125; catch(e) &#123;</span></span><br><span class="line"><span class="string">  console.log('</span>捕获到异常：<span class="string">',e);</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure></p>
<p>输出：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Uncaught <span class="built_in">SyntaxError</span>: Invalid or unexpected token</span><br></pre></td></tr></table></figure></p>
<blockquote>
<font color="#0e8a16">不过语法错误在我们开发阶段就可以看到，应该不会顺利上到线上环境吧。?</font>

</blockquote>
<p>3.异步错误<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="literal">undefined</span>.map(<span class="function"><span class="params">v</span> =&gt;</span> v);</span><br><span class="line">  &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我们看看日志：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Uncaught <span class="built_in">TypeError</span>: Cannot read property <span class="string">'map'</span> <span class="keyword">of</span> <span class="literal">undefined</span> at setTimeout (<span class="xml"><span class="tag">&lt;<span class="name">anonymous</span>&gt;</span>:3:11)</span></span><br></pre></td></tr></table></figure></p>
<p>并没有捕获到异常，这是需要我们特别注意的地方。<font color="#b60205">【try catch 无法捕获异步异常】</font></p>
<h1 id="四、window-onerror-不是万能的"><a href="#四、window-onerror-不是万能的" class="headerlink" title="四、window.onerror 不是万能的"></a>四、window.onerror 不是万能的</h1><p>当 JS 运行时错误发生时，window 会触发一个 ErrorEvent 接口的 error 事件，并执行 <code>window.onerror()</code>。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* @param &#123;String&#125;  message    错误信息</span></span><br><span class="line"><span class="comment">* @param &#123;String&#125;  source    出错文件</span></span><br><span class="line"><span class="comment">* @param &#123;Number&#125;  lineno    行号</span></span><br><span class="line"><span class="comment">* @param &#123;Number&#125;  colno    列号</span></span><br><span class="line"><span class="comment">* @param &#123;Object&#125;  error  Error对象（对象）</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>1.首先试试同步运行时错误<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line"><span class="comment">// message：错误信息（字符串）。</span></span><br><span class="line"><span class="comment">// source：发生错误的脚本URL（字符串）</span></span><br><span class="line"><span class="comment">// lineno：发生错误的行号（数字）</span></span><br><span class="line"><span class="comment">// colno：发生错误的列号（数字）</span></span><br><span class="line"><span class="comment">// error：Error对象（对象）</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line">Jartto;</span><br></pre></td></tr></table></figure></p>
<p>可以看到，我们捕获到了异常：<br><img src="/images/js-exception-handing-1.png" alt=""></p>
<p>2.再试试语法错误呢？<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> name = <span class="string">'Jartto</span></span><br></pre></td></tr></table></figure></p>
<p>控制台打印出了这样的异常：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Uncaught <span class="built_in">SyntaxError</span>: Invalid or unexpected token</span><br></pre></td></tr></table></figure></p>
<p id="div-border-top-red">什么，竟然没有捕获到语法错误？!!</p>

<p>3.怀着忐忑的心，我们最后来试试异步运行时错误：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  Jartto;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>控制台输出了：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">捕获到异常： &#123;<span class="attr">message</span>: <span class="string">"Uncaught ReferenceError: Jartto is not defined"</span>, </span><br><span class="line">  source: <span class="string">"http://127.0.0.1:8001/"</span>, </span><br><span class="line">  lineno: <span class="number">36</span>, <span class="attr">colno</span>: <span class="number">5</span>, </span><br><span class="line">  error: <span class="built_in">ReferenceError</span>: Jartto is not defined at setTimeout (http:<span class="comment">//127.0.0.1:8001/:36:5)&#125;</span></span><br></pre></td></tr></table></figure></p>
<p>4.接着，我们试试网络请求异常的情况：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;img src="./</span>jartto.png<span class="string">"&gt;</span></span><br></pre></td></tr></table></figure></p>
<p id="div-border-left-red">我们发现，不论是静态资源异常，或者接口异常，错误都无法捕获到。</p>

<p>补充一点：<code>window.onerror</code> 函数只有在返回 true 的时候，异常才不会向上抛出，否则即使是知道异常的发生控制台还是会显示 <code>Uncaught Error: xxxxx</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  Jartto;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>控制台就不会再有这样的错误了：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Uncaught <span class="built_in">ReferenceError</span>: Jartto is not defined</span><br><span class="line">    at setTimeout ((index):<span class="number">36</span>)</span><br></pre></td></tr></table></figure></p>
<font color="#ff8f00">需要注意：</font>

<ul>
<li>onerror 最好写在所有 JS 脚本的前面，否则有可能捕获不到错误；</li>
<li>onerror 无法捕获语法错误；</li>
</ul>
<p id="div-border-top-red">到这里基本就清晰了：在实际的使用过程中，<code>onerror</code> 主要是来捕获预料之外的错误，而 <code>try-catch</code> 则是用来在可预见情况下监控特定的错误，两者结合使用更加高效。</p>

<p id="div-border-left-yellow">问题又来了，捕获不到静态资源加载异常怎么办？</p>

<h1 id="五、window-addEventListener"><a href="#五、window-addEventListener" class="headerlink" title="五、window.addEventListener"></a>五、window.addEventListener</h1><p>当一项资源（如图片或脚本）加载失败，加载资源的元素会触发一个 Event 接口的 error 事件，并执行该元素上的 <code>onerror()</code> 处理函数。这些 error 事件不会向上冒泡到 window ，不过（至少在 Firefox 中）能被单一的 <code>window.addEventListener</code> 捕获。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;scritp&gt;</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'error'</span>, (error) =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, error);</span><br><span class="line">&#125;, <span class="literal">true</span>)</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;img src="./</span>jartto.png<span class="string">"&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>控制台输出：<br><img src="/images/js-exception-handing-2.png" alt=""></p>
<p>由于网络请求异常不会事件冒泡，因此必须在捕获阶段将其捕捉到才行，但是这种方式虽然可以捕捉到网络请求的异常，但是无法判断 HTTP 的状态是 404 还是其他比如 500 等等，所以还需要配合服务端日志才进行排查分析才可以。</p>
<font color="red">需要注意：</font>

<ul>
<li>不同浏览器下返回的 error 对象可能不同，需要注意兼容处理。</li>
<li>需要注意避免 addEventListener 重复监听。</li>
</ul>
<h1 id="六、Promise-Catch"><a href="#六、Promise-Catch" class="headerlink" title="六、Promise Catch"></a>六、Promise Catch</h1><p id="div-border-top-blue">在 promise 中使用 catch 可以非常方便的捕获到异步 error ，这个很简单。</p>

<p>没有写 catch 的 Promise 中抛出的错误无法被 onerror 或 try-catch 捕获到，所以我们务必要在 Promise 中不要忘记写 catch 处理抛出的异常。</p>
<p>解决方案： 为了防止有漏掉的 Promise 异常，建议在全局增加一个对 <code>unhandledrejection</code>的监听，用来全局监听 <code>Uncaught Promise Error</code>。使用方式：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>我们继续来尝试一下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  e.preventDefault()</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, e);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">Promise</span>.reject(<span class="string">'promise error'</span>);</span><br></pre></td></tr></table></figure></p>
<p>可以看到如下输出：<br><img src="/images/js-exception-handing-3.png" alt=""></p>
<p>那如果对 Promise 不进行 catch 呢？<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  e.preventDefault()</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, e);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">  reject(<span class="string">'jartto: promise error'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>嗯，事实证明，也是会被正常捕获到的。</p>
<p>所以，正如我们上面所说，为了防止有漏掉的 Promise 异常，建议在全局增加一个对 <code>unhandledrejection</code> 的监听，用来全局监听 <code>Uncaught Promise Error</code>。</p>
<p>补充一点：如果去掉控制台的异常显示，需要加上：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">event.preventDefault();</span><br></pre></td></tr></table></figure></p>
<h1 id="七、VUE-errorHandler"><a href="#七、VUE-errorHandler" class="headerlink" title="七、VUE errorHandler"></a>七、VUE errorHandler</h1><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Vue.config.errorHandler = <span class="function">(<span class="params">err, vm, info</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.error(<span class="string">'通过vue errorHandler捕获的错误'</span>);</span><br><span class="line">  <span class="built_in">console</span>.error(err);</span><br><span class="line">  <span class="built_in">console</span>.error(vm);</span><br><span class="line">  <span class="built_in">console</span>.error(info);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="八、React-异常捕获"><a href="#八、React-异常捕获" class="headerlink" title="八、React 异常捕获"></a>八、React 异常捕获</h1><p>React 16 提供了一个内置函数 componentDidCatch，使用它可以非常简单的获取到 react 下的错误信息<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">componentDidCatch(error, info) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(error, info);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>除此之外，我们可以了解一下：<code>error boundary</code><br>UI 的某部分引起的 JS 错误不应该破坏整个程序，为了帮 React 的使用者解决这个问题，React 16 介绍了一种关于错误边界（error boundary)的新观念。</p>
<p id="div-border-left-red">需要注意的是： error boundaries 并不会捕捉下面这些错误。</p>

<p>1.事件处理器<br>2.异步代码<br>3.服务端的渲染代码<br>4.在 error boundaries 区域内的错误</p>
<p>我们来举一个小例子，在下面这个 <code>componentDIdCatch(error,info)</code> 里的类会变成一个 <code>error boundary</code>：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ErrorBoundary</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props);</span><br><span class="line">    <span class="keyword">this</span>.state = &#123; <span class="attr">hasError</span>: <span class="literal">false</span> &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  componentDidCatch(error, info) &#123;</span><br><span class="line">    <span class="comment">// Display fallback UI</span></span><br><span class="line">    <span class="keyword">this</span>.setState(&#123; <span class="attr">hasError</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">    <span class="comment">// You can also log the error to an error reporting service</span></span><br><span class="line">    logErrorToMyService(error, info);</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.state.hasError) &#123;</span><br><span class="line">      <span class="comment">// You can render any custom fallback UI</span></span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Something went wrong.<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.props.children;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>然后我们像使用普通组件那样使用它：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;ErrorBoundary&gt;</span><br><span class="line">  &lt;MyWidget /&gt;</span><br><span class="line">&lt;<span class="regexp">/ErrorBoundary&gt;</span></span><br></pre></td></tr></table></figure></p>
<p><code>componentDidCatch()</code> 方法像 JS 的 <code>catch{}</code> 模块一样工作，但是对于组件，只有 class 类型的组件<code>( class component )</code>可以成为一个 <code>error boundaries</code>。</p>
<p>实际上，大多数情况下我们可以在整个程序中定义一个 <code>error boundary</code>组件，之后就可以一直使用它了！</p>
<h1 id="九、iframe-异常"><a href="#九、iframe-异常" class="headerlink" title="九、iframe 异常"></a>九、iframe 异常</h1><p>对于 iframe 的异常捕获，我们还得借力 <code>window.onerror</code>：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>一个简单的例子可能如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;iframe src=<span class="string">"./iframe.html"</span> frameborder=<span class="string">"0"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span></span><br><span class="line">&lt;script&gt;</span><br><span class="line">  <span class="built_in">window</span>.frames[<span class="number">0</span>].onerror = <span class="function"><span class="keyword">function</span> (<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'捕获到 iframe 异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;;</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br></pre></td></tr></table></figure></p>
<h1 id="十、Script-error"><a href="#十、Script-error" class="headerlink" title="十、Script error"></a>十、Script error</h1><p>一般情况，如果出现 <code>script error</code> 这样的错误，基本上可以确定是出现了跨域问题。这时候，是不会有其他太多辅助信息的，但是解决思路无非如下：</p>
<p id="div-border-left-blue">跨源资源共享机制( CORS )：我们为 script 标签添加 crossOrigin 属性。</p>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;script src=<span class="string">"http://jartto.wang/main.js"</span> crossorigin&gt;<span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<p>或者动态去添加 js 脚本：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>);</span><br><span class="line">script.crossOrigin = <span class="string">'anonymous'</span>;</span><br><span class="line">script.src = url;</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(script);</span><br></pre></td></tr></table></figure></p>
<p id="div-border-left-yellow">特别注意，服务器端需要设置：Access-Control-Allow-Origin </p>

<p>此外，我们也可以试试这个 - <a href="https://juejin.im/post/5c00a405f265da610e7fd024" target="_blank" rel="external">解决 Script Error 的另类思路</a>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> originAddEventListener = EventTarget.prototype.addEventListener;</span><br><span class="line">EventTarget.prototype.addEventListener = <span class="function"><span class="keyword">function</span> (<span class="params">type, listener, options</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> wrappedListener = <span class="function"><span class="keyword">function</span> (<span class="params">...args</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> listener.apply(<span class="keyword">this</span>, args);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">      <span class="keyword">throw</span> err;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> originAddEventListener.call(<span class="keyword">this</span>, type, wrappedListener, options);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>简单解释一下：</p>
<ul>
<li>改写了 EventTarget 的 addEventListener 方法；</li>
<li>对传入的 listener 进行包装，返回包装过的 listener，对其执行进行 try-catch；</li>
<li>浏览器不会对 try-catch 起来的异常进行跨域拦截，所以 catch 到的时候，是有堆栈信息的；</li>
<li>重新 throw 出来异常的时候，执行的是同域代码，所以 window.onerror 捕获的时候不会丢失堆栈信息；</li>
</ul>
<p>利用包装 addEventListener，我们还可以达到「扩展堆栈」的效果：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">   <span class="keyword">const</span> originAddEventListener = EventTarget.prototype.addEventListener;</span><br><span class="line">   EventTarget.prototype.addEventListener = <span class="function"><span class="keyword">function</span> (<span class="params">type, listener, options</span>) </span>&#123;</span><br><span class="line">+    <span class="comment">// 捕获添加事件时的堆栈</span></span><br><span class="line">+    <span class="keyword">const</span> addStack = <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`Event (<span class="subst">$&#123;type&#125;</span>)`</span>).stack;</span><br><span class="line">     <span class="keyword">const</span> wrappedListener = <span class="function"><span class="keyword">function</span> (<span class="params">...args</span>) </span>&#123;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">         <span class="keyword">return</span> listener.apply(<span class="keyword">this</span>, args);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">+        <span class="comment">// 异常发生时，扩展堆栈</span></span><br><span class="line">+        err.stack += <span class="string">'\n'</span> + addStack;</span><br><span class="line">         <span class="keyword">throw</span> err;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">return</span> originAddEventListener.call(<span class="keyword">this</span>, type, wrappedListener, options);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;)();</span><br></pre></td></tr></table></figure></p>
<h1 id="十一、崩溃和卡顿"><a href="#十一、崩溃和卡顿" class="headerlink" title="十一、崩溃和卡顿"></a>十一、崩溃和卡顿</h1><p>卡顿也就是网页暂时响应比较慢， JS 可能无法及时执行。但崩溃就不一样了，网页都崩溃了，JS 都不运行了，还有什么办法可以监控网页的崩溃，并将网页崩溃上报呢？</p>
<blockquote>
<p>崩溃和卡顿也是不可忽视的，也许会导致你的用户流失。</p>
</blockquote>
<p>1.利用 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控。<br>不错的文章，推荐阅读：<a href="http://jasonjl.me/blog/2015/06/21/taking-action-on-browser-crashes/" target="_blank" rel="external">Logging Information on Browser Crashes</a>。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'load'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  sessionStorage.setItem(<span class="string">'good_exit'</span>, <span class="string">'pending'</span>);</span><br><span class="line">  setInterval(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">      sessionStorage.setItem(<span class="string">'time_before_crash'</span>, <span class="keyword">new</span> <span class="built_in">Date</span>().toString());</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'beforeunload'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  sessionStorage.setItem(<span class="string">'good_exit'</span>, <span class="string">'true'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">if</span>(sessionStorage.getItem(<span class="string">'good_exit'</span>) &amp;&amp;</span><br><span class="line">  sessionStorage.getItem(<span class="string">'good_exit'</span>) !== <span class="string">'true'</span>) &#123;</span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">      insert crash logging code here</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  alert(<span class="string">'Hey, welcome back from your crash, looks like you crashed on: '</span> + sessionStorage.getItem(<span class="string">'time_before_crash'</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>2.基于以下原因，我们可以使用 <code>Service Worker</code> 来实现 <a href="https://juejin.im/entry/5be158116fb9a049c6434f4a?utm_source=gold_browser_extension" target="_blank" rel="external">网页崩溃的监控</a>：</p>
<ul>
<li>Service Worker 有自己独立的工作线程，与网页区分开，网页崩溃了，Service Worker 一般情况下不会崩溃；</li>
<li>Service Worker 生命周期一般要比网页还要长，可以用来监控网页的状态；</li>
<li>网页可以通过 <code>navigator.serviceWorker.controller.postMessage</code> API 向掌管自己的 SW 发送消息。</li>
</ul>
<h1 id="十二、错误上报"><a href="#十二、错误上报" class="headerlink" title="十二、错误上报"></a>十二、错误上报</h1><p>1.通过 Ajax 发送数据<br>因为 Ajax 请求本身也有可能会发生异常，而且有可能会引发跨域问题，一般情况下更推荐使用动态创建 img 标签的形式进行上报。</p>
<p>2.动态创建 img 标签的形式<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">report</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> reportUrl = <span class="string">'http://jartto.wang/report'</span>;</span><br><span class="line">  <span class="keyword">new</span> Image().src = <span class="string">`<span class="subst">$&#123;reportUrl&#125;</span>?logs=<span class="subst">$&#123;error&#125;</span>`</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>收集异常信息量太多，怎么办？实际中，我们不得不考虑这样一种情况：如果你的网站访问量很大，那么一个必然的错误发送的信息就有很多条，这时候，我们需要设置采集率，从而减缓服务器的压力：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Reporter.send = <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 只采集 30%</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">Math</span>.random() &lt; <span class="number">0.3</span>) &#123;</span><br><span class="line">    send(data)      <span class="comment">// 上报错误信息</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>采集率应该通过实际情况来设定，随机数，或者某些用户特征都是不错的选择。<font color="red">【这个随机数用的不错！】</font></p>
<h1 id="十三、总结"><a href="#十三、总结" class="headerlink" title="十三、总结"></a>十三、总结</h1><p id="div-border-left-yellow">回到我们开头提出的那个问题，如何优雅的处理异常呢？</p>

<p>1.可疑区域增加 Try-Catch<br>2.全局监控 JS 异常 window.onerror<br>3.全局监控静态资源异常 window.addEventListener<br>4.捕获没有 Catch 的 Promise 异常：unhandledrejection<br>5.VUE errorHandler 和 React componentDidCatch<br>6.监控网页崩溃：window 对象的 load 和 beforeunload<br>7.跨域 crossOrigin 解决</p>
<p>其实很简单，正如本文所说：采用组合方案，分类型的去捕获异常，这样基本 80%-90% 的问题都化于无形。</p>
<p><a href="http://jartto.wang/2018/11/20/js-exception-handling/" target="_blank" rel="external">原文</a></p>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

<blockquote class="blockquote-center" style="color: #ccc;">
    -------------本文结束 <i class="fa fa-apple"></i> 感谢您的阅读-------------
</blockquote>

  <span id="inline-green" style="border-radius:3px;">作者</span>：<a class="link-blue" href="https://github.com/Neveryu" target="_blank">Neveryu</a><br/>有问题请 <a class="link-blue" href="https://neveryu.github.io/guestbook" target="_blank">留言</a> 或者私信我的 <a class="link-blue" href="http://weibo.com/Neveryu" target="_blank">微博</a>。

  <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
    <div>满分是10分的话，这篇文章你给几分</div>
    <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
      <span>赏</span>
    </button>
    <div id="QR" style="display: none;">
      
        <div id="wechat" style="display: inline-block">
          <img id="wechat_qr" src="/reward/reward_wechat.png" alt="Never_yu WeChat Pay"/>
          <p>微信打赏</p>
        </div>
      
      
        <div id="alipay" style="display: inline-block">
          <img id="alipay_qr" src="/reward/reward_alipay.png" alt="Never_yu Alipay"/>
          <p>支付宝打赏</p>
        </div>
      
    </div>
  </div>


      
    </div>

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/JavaScript/" rel="tag"><i class="fa fa-tag"></i> JavaScript</a>
          
        </div>
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/03/31/vue-ssr/" rel="next" title="手把手教你 Vue 服务端渲染">
                <i class="fa fa-chevron-left"></i> 手把手教你 Vue 服务端渲染
              </a>
            
          </div>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/07/01/vue-element-change-theme/" rel="prev" title="基于Vue、ElementUI的换肤解决方案">
                基于Vue、ElementUI的换肤解决方案 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
        <!-- JiaThis Button BEGIN -->
<div class="jiathis_style">
  <a class="jiathis_button_tsina"></a>
  <a class="jiathis_button_tqq"></a>
  <a class="jiathis_button_weixin"></a>
  <a class="jiathis_button_cqq"></a>
  <a class="jiathis_button_douban"></a>
  <a class="jiathis_button_renren"></a>
  <a class="jiathis_button_qzone"></a>
  <a class="jiathis_button_kaixin001"></a>
  <a class="jiathis_button_copy"></a>
  <a href="http://www.jiathis.com/share" class="jiathis jiathis_txt jiathis_separator jtico jtico_jiathis" target="_blank"></a>
  <a class="jiathis_counter_style"></a>
</div>
<script type="text/javascript" >
  var jiathis_config={
    hideMore:false
  }
</script>
<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END -->

      
    </div>
  </div>


          </div>
          


          
        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel ">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/avatar/avatar.png"
               alt="Never_yu" />
          <p class="site-author-name" itemprop="name">Never_yu</p>
          <p class="site-description motion-element" itemprop="description">Stay Hungry,Stay Foolish</p>
        </div>
        <nav class="site-state motion-element">
          <div class="site-state-item site-state-posts">
            <a href="/archives">
              <span class="site-state-item-count">36</span>
              <span class="site-state-item-name">日志</span>
            </a>
          </div>

          
            <div class="site-state-item site-state-categories">
              <a href="/categories">
                <span class="site-state-item-count">4</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/tags">
                <span class="site-state-item-count">19</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        
          <div class="feed-link motion-element">
            <a href="/atom.xml" rel="alternate">
              <i class="fa fa-rss"></i>
              RSS
            </a>
          </div>
        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/Neveryu" target="_blank" title="Github">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                  Github
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://weibo.com/Neveryu" target="_blank" title="Weibo">
                  
                    <i class="fa fa-fw fa-weibo"></i>
                  
                  Weibo
                </a>
              </span>
            
          
        </div>

        
        
          <div class="cc-license motion-element" itemprop="license">
            <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" target="_blank">
              <img src="/images/cc-by-nc-sa.svg" alt="Creative Commons" />
            </a>
          </div>
        

        
        
          <div class="links-of-blogroll motion-element links-of-blogroll-inline">
            <div class="links-of-blogroll-title">
              <i class="fa  fa-fw fa-globe"></i>
              友情链接
            </div>
            <ul class="links-of-blogroll-list">
              
                <li class="links-of-blogroll-item">
                  <a href="https://codeigniter.org.cn/" title="CodeIgniter中国" target="_blank">CodeIgniter中国</a>
                </li>
              
                <li class="links-of-blogroll-item">
                  <a href="/weblog" title="建站日志" target="_blank">建站日志</a>
                </li>
              
                <li class="links-of-blogroll-item">
                  <a href="http://blog.csdn.net/csdn_yudong" title="CSDN" target="_blank">CSDN</a>
                </li>
              
                <li class="links-of-blogroll-item">
                  <a href="https://neveryu.github.io/neveryu" title="个人首页" target="_blank">个人首页</a>
                </li>
              
                <li class="links-of-blogroll-item">
                  <a href="/reward" title="打赏" target="_blank">打赏</a>
                </li>
              
            </ul>
          </div>
        
      </section>

      
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">
            
              
            
            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#一、为什么要处理异常？"><span class="nav-number">1.</span> <span class="nav-text">一、为什么要处理异常？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#二、需要处理哪些异常？"><span class="nav-number">2.</span> <span class="nav-text">二、需要处理哪些异常？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#三、Try-Catch-的误区"><span class="nav-number">3.</span> <span class="nav-text">三、Try-Catch 的误区</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#四、window-onerror-不是万能的"><span class="nav-number">4.</span> <span class="nav-text">四、window.onerror 不是万能的</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#五、window-addEventListener"><span class="nav-number">5.</span> <span class="nav-text">五、window.addEventListener</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#六、Promise-Catch"><span class="nav-number">6.</span> <span class="nav-text">六、Promise Catch</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#七、VUE-errorHandler"><span class="nav-number">7.</span> <span class="nav-text">七、VUE errorHandler</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#八、React-异常捕获"><span class="nav-number">8.</span> <span class="nav-text">八、React 异常捕获</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#九、iframe-异常"><span class="nav-number">9.</span> <span class="nav-text">九、iframe 异常</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十、Script-error"><span class="nav-number">10.</span> <span class="nav-text">十、Script error</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十一、崩溃和卡顿"><span class="nav-number">11.</span> <span class="nav-text">十一、崩溃和卡顿</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十二、错误上报"><span class="nav-number">12.</span> <span class="nav-text">十二、错误上报</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十三、总结"><span class="nav-number">13.</span> <span class="nav-text">十三、总结</span></a></li></ol></div>
            
          </div>
        </section>
      

      
        <section style="border-top:1px dotted #ccc; padding-top:10px;">
          <iframe width="100%" height="120" class="share_self"  frameborder="0" scrolling="no" src="https://widget.weibo.com/weiboshow/index.php?language=&width=0&height=120&fansRow=2&ptype=1&speed=0&skin=5&isTitle=1&noborder=1&isWeibo=0&isFans=0&uid=5346488237&verifier=d529ff3a&dpc=1"></iframe>
        </section>
      
    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        

<div class="busuanzi-count">

  <!-- <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script> -->
  <!-- 上面这个是之前的，不知道为什么失效了，改成下面这个 -->
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">您是第<span class="busuanzi-value" id="busuanzi_value_site_uv"></span>个小伙伴</span>
  

  
    <span class="site-pv">本站总浏览<span class="busuanzi-value" id="busuanzi_value_site_pv"></span>次</span>
  
  
</div>



        <div class="copyright" >
  
  &copy;  2016 - 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Never_yu</span>
</div>

<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io" rel="external nofollow">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next" rel="external nofollow">
    NexT.Pisces
  </a>
</div>

<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count" style="color: #e90f92;">全站共 156k 字</span>
</div>
        
      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  



  
  <script type="text/javascript" src="/vendors/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/vendors/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/vendors/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/vendors/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/vendors/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/vendors/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.0.1"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.0.1"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.0.1"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.0.1"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.0.1"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.0.1"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.0.1"></script>



  



  




  
  
  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length == 0) {
       search_path = "search.xml";
    }
    var path = "/" + search_path;
    // monitor main search box;

    function proceedsearch() {
      $("body").append('<div class="popoverlay">').css('overflow', 'hidden');
      $('.popup').toggle();

    }
    // search function;
    var searchFunc = function(path, search_id, content_id) {
    'use strict';
    $.ajax({
        url: path,
        dataType: "xml",
        async: true,
        success: function( xmlResponse ) {
            // get the contents from search data
            isfetched = true;
            $('.popup').detach().appendTo('.header-inner');
            var datas = $( "entry", xmlResponse ).map(function() {
                return {
                    title: $( "title", this ).text(),
                    content: $("content",this).text(),
                    url: $( "url" , this).text()
                };
            }).get();
            var $input = document.getElementById(search_id);
            var $resultContent = document.getElementById(content_id);
            $input.addEventListener('input', function(){
                var matchcounts = 0;
                var str='<ul class=\"search-result-list\">';
                var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                $resultContent.innerHTML = "";
                if (this.value.trim().length > 1) {
                // perform local searching
                datas.forEach(function(data) {
                    var isMatch = true;
                    var content_index = [];
                    var data_title = data.title.trim().toLowerCase();
                    var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
                    var data_url = data.url;
                    var index_title = -1;
                    var index_content = -1;
                    var first_occur = -1;
                    // only match artiles with not empty titles and contents
                    if(data_title != '' && data_content != '') {
                        keywords.forEach(function(keyword, i) {
                            index_title = data_title.indexOf(keyword);
                            index_content = data_content.indexOf(keyword);
                            if( index_title < 0 && index_content < 0 ){
                                isMatch = false;
                            } else {
                                if (index_content < 0) {
                                    index_content = 0;
                                }
                                if (i == 0) {
                                    first_occur = index_content;
                                }
                            }
                        });
                    }
                    // show search results
                    if (isMatch) {
                        matchcounts += 1;
                        str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
                        var content = data.content.trim().replace(/<[^>]+>/g,"");
                        if (first_occur >= 0) {
                            // cut out 100 characters
                            var start = first_occur - 20;
                            var end = first_occur + 80;
                            if(start < 0){
                                start = 0;
                            }
                            if(start == 0){
                                end = 50;
                            }
                            if(end > content.length){
                                end = content.length;
                            }
                            var match_content = content.substring(start, end);
                            // highlight all keywords
                            keywords.forEach(function(keyword){
                                var regS = new RegExp(keyword, "gi");
                                match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
                            });

                            str += "<p class=\"search-result\">" + match_content +"...</p>"
                        }
                        str += "</li>";
                    }
                })};
                str += "</ul>";
                if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
                if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
                $resultContent.innerHTML = str;
            });
            proceedsearch();
        }
    });}

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched == false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };

    });

    $('.popup-btn-close').click(function(e){
      $('.popup').hide();
      $(".popoverlay").remove();
      $('body').css('overflow', '');
    });
    $('.popup').click(function(e){
      e.stopPropagation();
    });
  </script>


  

  

  
<script type="text/javascript" async src="//push.zhanzhang.baidu.com/push.js">
</script>


  <!-- 按需加载背景 -->
  <!-- 背景动画 -->
<script type="text/javascript">
  // 按需加载背景
  // 如果是all，就直接加载了
  if("pc" == "all") {
    $.getScript("/js/src/particle.js?v=5.0.1");
  }
  // 识别手机或电脑的js开始
  (function(){
    var res = GetRequest();
    var par = res['index'];
    if(par!='gfan'){
      var ua=navigator.userAgent.toLowerCase();
      var contains=function (a, b){
          if(a.indexOf(b)!=-1){return true;}
      };
      if((contains(ua,"android") && contains(ua,"mobile"))||(contains(ua,"android") && contains(ua,"mozilla"))||(contains(ua,"android") && contains(ua,"opera"))||contains(ua,"ucweb7")||contains(ua,"iphone")){
        return false;
      } else {
        $.getScript("/js/src/particle.js?v=5.0.1");
      }
    }
  })();
  function GetRequest() {
    var url = location.search;
    var theRequest = new Object();
    if (url.indexOf("?") != -1) {
      var str = url.substr(1);
      strs = str.split("&");
      for(var i = 0; i < strs.length; i ++) {
        theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
      }
    }
    return theRequest;
  }
</script>
<!-- 识别手机或电脑的js结束 -->  

  <!-- 页面点击小红心 -->
  <!-- 页面点击小红心 -->

  <script type="text/javascript" src="/js/src/love.js?v=5.0.1"></script>


  <!-- 鼠标移动，效果 -->
  <!-- 鼠标移动特效 -->

  <script type="text/javascript" src="/js/src/jquery-stars.js?v=5.0.1"></script>
  <script type="text/javascript">
  jQuery('body').jstars({
  	image_path: '/images',
  	image: 'candy-cane-stars.png',
  	style: 'white',
  	width: 34,
  	height: 34,
  	delay: 700,
  	frequency: 5
  });
  </script>


  <!-- 页面 title 进入/离开 效果 -->

  <script type="text/javascript">var OriginTitile=document.title,st;document.addEventListener("visibilitychange",function(){document.hidden?(document.title="≡[。。]≡ 回不去了吗？!",clearTimeout(st)):(document.title="(ฅ>ω<*ฅ) Thank Vue~ "+OriginTitile,st=setTimeout(function(){document.title=OriginTitile},4e3))})</script>


</body>
</html>
